import pandas as pd
import numpy as np
#!pip install pandas -U ## 코랩 기본 판다스가 1.5.3이라 업데이트 해야 해당되는 내용임... jupyter lab에는 정상적으로 작동함...Pandas | 매핑
apply,map,applymap에 대하여 자세히 알아보자.
1. 라이브러리 imports
2. apply
A. Motive
- 아래와 같은 상황이 있었다.
df = pd.DataFrame({'A':[1,2,3,4]})
df| A | |
|---|---|
| 0 | 1 |
| 1 | 2 |
| 2 | 3 |
| 3 | 4 |
df[['A']].apply(np.mean)A 2.5
dtype: float64
df['A'].apply(np.mean)0 1.0
1 2.0
2 3.0
3 4.0
Name: A, dtype: float64
### B. s.apply()
- 내부 입력이 가능한 함수는 아래와 같다.
- 변환함수(스칼라 입력, 스칼라 출력) : 로그, 제곱
- 변환함수(벡터 입력, 벡터 출력) : 표준화(
StandardScaler()?), 정렬(sort_values()) - 집계함수(벡터 입력, 스칼라 출력) : 평균, 최댓값
여기서 쓸모있는 건 “1”뿐이다.
# 예제 1 s.apply + 스칼라 입력, 스칼라 출력
s = pd.Series([1,2,3])
s0 1
1 2
2 3
dtype: int64
s.apply(lambda x : -x)0 -1
1 -2
2 -3
dtype: int64
이건 사실 아래의 동작으로 이해하면 된다.
1 -> -1
2 -> -2
3 -> -3
굳이 따지면 이런 느낌
[(lambda x : -x)(i) for i in s][-1, -2, -3]
# 예제 2 s.apply + 벡터 입력 / 스칼라 출력
s.apply(np.sum)0 1
1 2
2 3
dtype: int64
사실상 스칼라 입력, 스칼라 출력으로 해석해야 한다. (각 개체값들이 벡터로 들어간 게 아닌 이상…)
- 에러는 안나지만 원하는 동작은 아님. (원한 건 전부 합하는 거야…)
이것은 사실 아래의 동작으로 이해할 수 있다.
1 -> sum(1) = 1
2 -> sum(2) = 2
3 -> sum(3) = 3
코드로는 아래의 느낌
[np.sum(i) for i in s][1, 2, 3]
# 예제 3 s.apply + 벡터 입력 / 벡터 출력
s = pd.Series([1,2,3])
s0 1
1 2
2 3
dtype: int64
s.apply(lambda x : x-np.mean(x))0 0.0
1 0.0
2 0.0
dtype: float64
- 이것도 원하는 동작은 아닌데 에러도 나지 않음…
이것은 사실 아래의 동작으로 이해할 수 있다.
1 -> 1-mean(1) = 0
2 -> 2-mean(2) = 0
3 -> 3-mean(3) = 0
코드로는 아래의 느낌
[i-np.mean(i) for i in s][0.0, 0.0, 0.0]
C. df.apply()
- 가능한 형태는 아래와 같다.
- 변환함수(벡터 입력, 벡터 출력) : 표준화, 정렬
- 집계함수(벡터 입력, 스칼라 출력) : 평균, 최댓값
쓸모있는 건 “1”,“2” 모두이다.
# 예제 1 df.apply + 스칼라 입력 / 스칼라 출력(불가능)
df = pd.DataFrame({'X':[0.1,0.2,0.3],'Y':[-0.1,-0.2,-0.3]})
df| X | Y | |
|---|---|---|
| 0 | 0.1 | -0.1 |
| 1 | 0.2 | -0.2 |
| 2 | 0.3 | -0.3 |
df.apply(lambda x : 'pos' if x > 0 else 'neg')ValueError: ignored
불가하다.
- apply() 작동기전 :
[(lambda x : 'pos' if x > 0 else 'neg')(df[i]) for i in df]
# 예제 2 df.apply + ~스칼라 입력, 스칼라 출력~ 벡터 입력, 벡터 출력!
df = pd.DataFrame({'X':[0.1,0.2,0.3],'Y':[-0.1,-0.2,-0.3]})
df| X | Y | |
|---|---|---|
| 0 | 0.1 | -0.1 |
| 1 | 0.2 | -0.2 |
| 2 | 0.3 | -0.3 |
df.apply(lambda x : x+1)| X | Y | |
|---|---|---|
| 0 | 1.1 | 0.9 |
| 1 | 1.2 | 0.8 |
| 2 | 1.3 | 0.7 |
이것은 사실 아래의 동작으로 이해할 수 있다.
df['X'] -> (df['X'])+2
df['Y'] -> (df['Y'])+2
코드로는 아래의 느낌이다.
[(lambda x : x+1)(df[i]) for i in df][0 1.1
1 1.2
2 1.3
Name: X, dtype: float64,
0 0.9
1 0.8
2 0.7
Name: Y, dtype: float64]
# 예제 3 df.apply + 벡터 입력, 스칼라 출력(집계함수)
df = pd.DataFrame({'X':[0.1,0.2,0.3],'Y':[-0.1,-0.2,-0.3]})
df| X | Y | |
|---|---|---|
| 0 | 0.1 | -0.1 |
| 1 | 0.2 | -0.2 |
| 2 | 0.3 | -0.3 |
df.apply(np.sum)X 0.6
Y -0.6
dtype: float64
# 예제 4 + 벡터 입력, 스칼라 출력(집계함수) 2 : axis = 0, 1
df.apply(np.sum, axis = 1)0 0.0
1 0.0
2 0.0
dtype: float64
s.apply에서는axis가 유효한 인자는 아니지만,df.apply에선 유효한 입력이고 디폴트는 0이다.
# 예제 5 df.apply + 벡터 입력, 벡터 출력(axis 옵션에 따른 차이)
df = pd.DataFrame({'X':[1,2,3],'Y':[4,5,6]})
df| X | Y | |
|---|---|---|
| 0 | 1 | 4 |
| 1 | 2 | 5 |
| 2 | 3 | 6 |
df.apply(lambda x : x - np.mean(x))| X | Y | |
|---|---|---|
| 0 | -1.0 | -1.0 |
| 1 | 0.0 | 0.0 |
| 2 | 1.0 | 1.0 |
df.apply(lambda x : x - np.mean(x), axis = 1)| X | Y | |
|---|---|---|
| 0 | -1.5 | 1.5 |
| 1 | -1.5 | 1.5 |
| 2 | -1.5 | 1.5 |
# 예제 6 df.apply + 벡터 입력, 벡터 출력(sorting)
df = pd.DataFrame({'X':[ 3.285, 0.328, -1.261],'Y':[ 1.068, 0.145, -0.222]})
df| X | Y | |
|---|---|---|
| 0 | 3.285 | 1.068 |
| 1 | 0.328 | 0.145 |
| 2 | -1.261 | -0.222 |
df.apply(np.sort)| X | Y | |
|---|---|---|
| 0 | -1.261 | -0.222 |
| 1 | 0.328 | 0.145 |
| 2 | 3.285 | 1.068 |
df.apply(np.sort, axis = 1)0 [1.068, 3.285]
1 [-1.261, 0.145]
2 [-0.222, 0.328]
dtype: object
각 row 별로 함수를 걸어주게 되면 결과가 판다스 시리즈로 나오지 않음… 그걸 다시 묶으면 데이터프레임이 나올 수가 없게 된다.
df.apply(lambda x: x*0+np.sort(x), axis=1) # x*0을 추가하여 시리즈 형식을 유지해줬다. 그다지 안중요한 트릭..| X | Y | |
|---|---|---|
| 0 | 1.068 | 3.285 |
| 1 | -1.261 | 0.145 |
| 2 | -0.222 | 0.328 |
시리즈로 묶어서 s.apply()로 하는 게 가장 적합하다.
3. map
- 그냥 모든 원소에 동일 적용
### A. s.map()
- s.apply()와 거의 똑같다.
- 변환함수(스칼라입력,스칼라출력): 로그, 제곱
- 변환함수(벡터입력,벡터출력): 표준화, 정렬
- 집계함수(벡터입력,스칼라출력): 평균, 최대값
- 딕셔너리!!!
쓸모있는 건 “1”, “4”이다. 특히 “4”는 특정상황에서 매우 쓸모있음.
# 예제 1 쓰는 거. s.map + 스칼라 입력, 스칼라 출력(소문자로)
s = pd.Series(['A','B','B','B','A'])
s0 A
1 B
2 B
3 B
4 A
dtype: object
s.map(lambda x: x.lower())0 a
1 b
2 b
3 b
4 a
dtype: object
# 예제 2 s.map + 스칼라 입력, 스칼라 출력(굳이 쓸 일 없음. apply써도 동일)
s = pd.Series([1,3,4,2])
s0 1
1 3
2 4
3 2
dtype: int64
s.map(lambda x : x**2)0 1
1 9
2 16
3 4
dtype: int64
# 예제 3 s.map + 벡터 입력, 스칼라 출력 > 가능은 한데, 사실 스칼라 입력, 스칼라 출력으로 해석해야 하고, 쓸모없음.
s = pd.Series([1,3,4,2])
s.map(np.sum)0 1
1 3
2 4
3 2
dtype: int64
# 예제 4 쓰는 거. s.map + 딕셔너리
s = pd.Series(['A','B','B','B','A'])
s0 A
1 B
2 B
3 B
4 A
dtype: object
s.map({'A':'A+','B':'B0'})0 A+
1 B0
2 B0
3 B0
4 A+
dtype: object
B. df.map() = df.applymap()
- 가능한 형태는 아래와 같다.
- 변환함수(스칼라 입력, 스칼라 출력) : 로그, 제곱
- 집계함수(벡터 입력, 스칼라 출력)
“1”만 쓸모가 있다. 여기서 df.map(변환함수)는 df.applymap(변환함수)와 기능이 같다.
# 예제 1 df.map + 스칼라 입력, 스칼라 출력
df = pd.DataFrame({'A':[2143,2143],'B':['-',3456]})
df| A | B | |
|---|---|---|
| 0 | 2143 | - |
| 1 | 2143 | 3456 |
지랄맞은 형태
df.map(lambda x: 0 if x == '-' else x) ## 왜 코랩은 map 안됨???| A | B | |
|---|---|---|
| 0 | 2143 | 0 |
| 1 | 2143 | 3456 |
# 예제 2 df.map + 벡터 입력, 벡터 출력 : 불가능함
df = pd.DataFrame({'A':np.random.randn(5), 'B':np.random.randn(5)+5})
df| A | B | |
|---|---|---|
| 0 | 0.385620 | 8.123319 |
| 1 | 2.016558 | 5.388919 |
| 2 | -0.187575 | 7.458609 |
| 3 | -0.277671 | 4.883501 |
| 4 | -0.070624 | 4.129147 |
df.map(np.sort) # 불가능..AxisError: ignored
# 예제 3 df.map + 벡터 입력, 스칼라 출력(집계함수) >> 사실상 스칼라 입력, 스칼라 출력. 의미없음
df = pd.DataFrame({'A':np.random.randn(5), 'B':np.random.randn(5)+5})
df| A | B | |
|---|---|---|
| 0 | -2.705998 | 3.788923 |
| 1 | 0.944862 | 5.549552 |
| 2 | -0.405228 | 4.180423 |
| 3 | 0.594811 | 4.114496 |
| 4 | 0.004424 | 4.233611 |
df.map(np.mean)| A | B | |
|---|---|---|
| 0 | -2.705998 | 3.788923 |
| 1 | 0.944862 | 5.549552 |
| 2 | -0.405228 | 4.180423 |
| 3 | 0.594811 | 4.114496 |
| 4 | 0.004424 | 4.233611 |
# 예제 4 df.map + 딕셔너리(불가능함)
df = pd.DataFrame({'guebin':[0,1,0,1,0,1],'hynn':[0,1,1,1,1,1]})
df| guebin | hynn | |
|---|---|---|
| 0 | 0 | 0 |
| 1 | 1 | 1 |
| 2 | 0 | 1 |
| 3 | 1 | 1 |
| 4 | 0 | 1 |
| 5 | 1 | 1 |
df.map({0:'fail',1:'pass'})TypeError: ignored